
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "expression.h"
#include "function.h"
#include "symtab.h"


struct expression_node* create_expression_node(struct expression* ex)
{
    struct expression_node* res = (struct expression_node*)malloc(sizeof(struct expression_node));
    res->data = ex;
    res->next = NULL;
    return res;
}

struct string_node* create_string_node(const char* nm)
{
    struct string_node* res = (struct string_node*)malloc(sizeof(struct string_node));
    res->data = (char*)malloc(1+strlen(nm));
    strcpy(res->data, nm);
    res->next = NULL;
    return res;
}

struct lambda* create_lambda(struct string_node* names,struct expression_node* exprs)
{
    // get space for object
    struct lambda* res = (struct lambda*)malloc(sizeof(struct lambda));
    // copy list of arguments
    struct string_node* p = names;
    int sz = 0;
    while( p != NULL ) { ++sz; p = p->next; }
    res->paramc = sz;
    res->params = (char**)malloc(res->paramc * sizeof(char*));
    p = names;
    for(int i = 0; i < res->paramc; ++i) {
        res->params[i] = (char*)malloc(strlen(p->data) + 1);
        strcpy(res->params[i], p->data);
        p = p->next;
    }
    // copy list of body
    struct expression_node* e = exprs;
    sz = 0;
    while( e != NULL ) { ++sz; e = e->next; }
    res->bodyc = sz;
    res->bodies = (struct expression**)malloc(res->bodyc * sizeof(struct expression*));
    e = exprs;
    for(int i = 0; i < res->bodyc; ++i) {
        res->bodies[i] = e->data;
        e = e->next;
    }
    return res;
}

void destroy_lambda(struct lambda* fn)
{
    for(int i = 0; i < fn->paramc; ++i)
        free(fn->params[i]);
    free(fn->params);
    for(int i = 0; i < fn->bodyc; ++i)
        destroy_expression(fn->bodies[i]);
    free(fn->bodies);
    free(fn);
}

struct funcall* create_funcall(const char* fname, struct expression_node* ags)
{
    struct funcall* res = (struct funcall*)malloc(sizeof(struct funcall));
    // copy name
    res->func = (char*)malloc(strlen(fname) + 1);
    strcpy(res->func, fname);
    // copy arguments
    struct expression_node* e = ags;
    res->argc = 0;
    while( e != NULL ) { ++res->argc; e = e->next; }
    res->args = (struct expression**)malloc(res->argc * sizeof(struct expression*));
    e = ags;
    for(int i = 0; i < res->argc; ++i) {
        res->args[i] = e->data;
        e = e->next;
    }
    return res;
}

void destroy_funcall(struct funcall* fc)
{
    free(fc->func);
    for(int i = 0; i < fc->argc; ++i)
        destroy_expression(fc->args[i]);
    free(fc);
}

double evaluate_funcall(struct funcall* fc, struct symbol_table* env)
{
    // get lambda object from environment
    struct symbol* sym = search_symbol(env, fc->func);    
    assert(sym != NULL);
    struct lambda* function = sym->value.func;
    assert(function != NULL);
    // create local symbol table
    struct symbol_table* locals = create_symbol_table();
    for(int i = 0; i < function->paramc; ++i) {
        double num = evaluate(fc->args[i], env);
        struct expression* temp0 = create_constanta(num);
        assert(temp0 != NULL);
        struct symbol* temp1 = create_expression_symbol(function->params[i], temp0);
        assert(temp1 != NULL);
        insert_symbol(locals, temp1);
    }
    
    // concatenating local and global symbol tables
    locals->end->link = env->begin;
    // evaluate function body ...
    double result = 0;
    for(int e = 0; e < function->bodyc; ++e)
        result = evaluate(function->bodies[e], locals);
    // disconnect local table from global
    locals->end->link = NULL;
    destroy_symbol_table(locals);
    // and return value of last evaluated expression
    return result;
}
